/*
 * Copyright (C) 2012 Rothens
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package rpg;

import rpg.mouse.MListener;
import rpg.game.SingleGame;
import java.awt.*;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.File;
import java.util.Calendar;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import rpg.camera.CELetterbox;
import rpg.camera.CEText;
import rpg.camera.CMGoto;
import rpg.camera.CMJumpto;
import rpg.camera.Camera;
import rpg.camera.Cinematic;
import rpg.gfx.FadeEffect;
import rpg.gfx.FontSets;
import rpg.gfx.MessageScreen;
import rpg.gfx.Overlay;
import rpg.game.GameScreen;
import rpg.gfx.Bitmap;
import rpg.gfx.menu.IMenu;
import rpg.gfx.menu.MainMenu;
import rpg.util.Preferences;

/**
 * rothens.tumblr.com
 *
 * @author Rothens
 */
public class RPG extends Canvas implements Runnable {

    public enum RPGPhase {

        MENU,
        GAME,
        EDITOR,
        MESSAGE_SCREEN;
    }
    private boolean running;
    private int FPS = 0;
    private GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
    private GraphicsDevice device = env.getDefaultScreenDevice();
    private GraphicsConfiguration config = device.getDefaultConfiguration();
    /**
     * Layers - start
     */
    private BufferedImage gameLayer;
    private int[] glPixels;
    private BufferedImage overLayer;
    private int[] olPixels;
    /**
     * Layers - end
     */
    private SingleGame game;
    private GameScreen screen;
    private Overlay overlay;
    private IMenu menu;
    private MessageScreen mscreen = null;
    private RPGPhase phase = RPGPhase.MENU;
    private Thread thread;
    private static JFrame frame;
    public static int WINDOW_WIDTH = 800;
    public static int WINDOW_HEIGHT = 600;
    public static final String version = "0.0.16";
    public static RPG rpg;
    public static KListener keylistener = KListener.getInstance();
    public static MListener mouselistener = MListener.getInstance();
    public static FontSets fs = new FontSets();
    public static Dimension size;

    public RPG() {
        Preferences.getInstance();
        size = new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT);
        setSize(size);
        setMinimumSize(size);
        setMaximumSize(size);
        setPreferredSize(size);
        setFocusable(false);
        addMouseListener(mouselistener);

        screen = new GameScreen(WINDOW_WIDTH, WINDOW_HEIGHT);

        menu = new MainMenu(WINDOW_WIDTH, WINDOW_HEIGHT);
        addMouseMotionListener(mouselistener);

        gameLayer = new BufferedImage(WINDOW_WIDTH, WINDOW_HEIGHT, BufferedImage.TYPE_INT_RGB);
        glPixels = ((DataBufferInt) gameLayer.getRaster().getDataBuffer()).getData();

        overlay = new Overlay(WINDOW_WIDTH, WINDOW_HEIGHT);

        overLayer = new BufferedImage(WINDOW_WIDTH, WINDOW_HEIGHT, BufferedImage.TYPE_INT_ARGB);
        olPixels = ((DataBufferInt) overLayer.getRaster().getDataBuffer()).getData();

    }

    public static void main(String[] args) {
        rpg = new RPG();

        frame = new JFrame("2D RPG Engine " + version);
        GridLayout g = new GridLayout();
        frame.setLayout(g);

        frame.getContentPane().add(rpg);

        frame.setResizable(false); //we shouldn't allow resize manually
        frame.setLocationRelativeTo(null); //center the frame on the user's screen
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.addKeyListener(keylistener);
        frame.setFocusTraversalKeysEnabled(false);
        frame.addMouseListener(mouselistener);

        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        rpg.start();
    }

    public void newGame() {
        //overlay.addOverlay(new DayNightEffect(WINDOW_WIDTH,WINDOW_HEIGHT,0x0));
        overlay.addOverlay(new FadeEffect(WINDOW_WIDTH, WINDOW_HEIGHT, true, 10));
        keylistener.clear();
        phase = RPGPhase.GAME;
        menu = null;
        SingleGame.newInstance();
        game = SingleGame.getInstance();
        
        Camera cam = game.getCamera();
        Cinematic c = new Cinematic();
        c.addMovement(new CMJumpto(0, 0, cam));
        c.addMovement(new CMGoto(cam.getFollowed(), 0.1f, cam));
        c.addEffect(new CELetterbox(373).setPriority(99), 0);
        c.addEffect(new CEText("Üdvözöllek §ff0000 Rothens §ffff00 kicsi világában.", 200), 60);

        cam.addCinematic(c);

    }

    public void gotoMenu(IMenu m) {
        keylistener.clear();
        phase = RPGPhase.MENU;
        menu = m;
    }

    public void playerDeath() {
        phase = RPGPhase.MESSAGE_SCREEN;
        keylistener.clear();
        mscreen = new MessageScreen("Game Over", 0x0, (byte) 0, 2) {
            @Override
            public void skip() {
                gotoMenu(new MainMenu(WINDOW_WIDTH, WINDOW_HEIGHT));
            }
        };
    }

    public synchronized void start() {
        if (running) {
            return;
        }
        running = true;
        thread = new Thread(this);
        thread.start();
    }

    public synchronized void stop() {
        if (!running) {
            return;
        }
        running = false;
        try {
            thread.join();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public void run() {

        double upSec = 0;
        long lastTime = System.nanoTime();
        double secPerTick = 1 / 60.0; //60 FPS
        int cnt = 0;
        int frames = 0;

        while (running) {

            //FPS limit woohoo :D
            long now = System.nanoTime();
            long passed = now - lastTime;
            lastTime = now;

            if (passed < 0) {
                passed = 0;
            }

            if (passed > 100000000) {
                passed = 100000000;
            }

            upSec += passed / 1000000000.0;

            boolean ticked = false;

            while (upSec > secPerTick) {
                tick();
                upSec -= secPerTick;
                ticked = true;
                cnt++;
                if (cnt % 60 == 0) {
                    FPS = frames;
                    lastTime += 1000;
                    frames = 0;
                }
            }
            if (ticked) {
                render();
                frames++;
            }
        }

    }

    private void tick() {
        switch (phase) {
            case MENU:
                tickMenu();
                break;
            case GAME:
                tickGame();
                break;
            case EDITOR:
                break;
            case MESSAGE_SCREEN:
                tickScreen();
                break;

        }
    }

    private void render() {
        switch (phase) {
            case MENU:
                renderMenu();
                break;
            case GAME:
                renderGame();
                break;
            case EDITOR:
                break;
            case MESSAGE_SCREEN:
                renderScreen();
                break;

        }
    }

    private void tickScreen() {
        mscreen.tick();
        keylistener.tick();
        mouselistener.tick();
    }

    private void renderScreen() {
        BufferStrategy bfs = this.getBufferStrategy();
        if (bfs == null) {
            createBufferStrategy(2);
            return;
        }
        mscreen.render();
        System.arraycopy(mscreen.pixels, 0, glPixels, 0, WINDOW_WIDTH * WINDOW_HEIGHT);
        Graphics g = bfs.getDrawGraphics();
        //clear the screen
        g.setColor(Color.black);

        g.fillRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);

        g.drawImage(gameLayer, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, null);
        g.dispose();

        bfs.show();
    }

    private void tickGame() {
        SingleGame.getInstance().tick();
        keylistener.tick();
        mouselistener.tick();
    }

    private void renderGame() {
        BufferStrategy bfs = this.getBufferStrategy();
        if (bfs == null) {
            createBufferStrategy(3);
            return;
        }
        screen.render(game);
        overlay.render();
        screen.drawOnBlend(overlay, 0, 0);
        Bitmap b = SingleGame.getInstance().getEffects();
        if (b != null) {
            screen.drawOnBlend(b, 0, 0);
        }
        System.arraycopy(screen.pixels, 0, glPixels, 0, WINDOW_WIDTH * WINDOW_HEIGHT);
        Graphics2D g = (Graphics2D) bfs.getDrawGraphics();
        g.drawImage(gameLayer, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, null);
        g.setColor(Color.RED);
        g.dispose();

        bfs.show();
    }

    private void tickMenu() {
        menu.tick();
        keylistener.tick();
        mouselistener.tick();
    }

    private void renderMenu() {
        BufferStrategy bfs = this.getBufferStrategy();
        if (bfs == null) {
            createBufferStrategy(2);
            return;
        }
        menu.render();
        System.arraycopy(menu.getGraphics().pixels, 0, glPixels, 0, WINDOW_WIDTH * WINDOW_HEIGHT);
        Graphics g = bfs.getDrawGraphics();
        //clear the screen
        g.setColor(Color.black);

        g.fillRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);

        g.drawImage(gameLayer, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, null);
        g.dispose();

        bfs.show();
    }

    public void screenShot() {
        try {
            File f = new File("screenshot");
            f.mkdir();
            Rectangle rectangle = frame.getBounds();
            Robot robot = new Robot();
            BufferedImage image = robot.createScreenCapture(rectangle);
            File file = getPossiblePNG();
            ImageIO.write(image, "png", file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String getTimeString() {
        Calendar c = Calendar.getInstance();
        String ret = c.get(Calendar.YEAR) + "-" + c.get(Calendar.MONTH) + "-" + c.get(Calendar.DAY_OF_MONTH)
                + "_" + c.get(Calendar.HOUR_OF_DAY) + "." + c.get(Calendar.MINUTE) + "." + c.get(Calendar.SECOND);
        return ret;
    }

    public File getPossiblePNG() {
        String s = getTimeString();
        File f = new File("screenshot" + File.separator + s + ".png");
        int cnt = 1;
        while (f.exists()) {
            f = new File("screenshot" + File.separator + s + "(" + cnt + ").png");
            ++cnt;
        }
        return f;
    }
}
